/* 
 * File:   Triangle.cpp
 * Author: brady
 * 
 * Created on August 7, 2011, 3:47 PM
 */

#include <shapes/Triangle.h>
#include <core/Vertex.h>

Triangle::Triangle() {
    v1=v2=v3=NULL;
}

Triangle::Triangle(Vertex* v1, Vertex* v2, Vertex* v3){
    this->v1 = v1;
    this->v2 = v2;
    this->v3 = v3;
    buildBoundingBox();
}

Triangle::~Triangle() {
}

bool Triangle::testIntersect(IntersectionParams& ip, IntersectionResult& ir){
    ir.shape = NULL;
    const Ray& ray = ip.getRay();
    double tmin, tmax;
    tmin = ip.getTMin();
    tmax = ip.getTMax();
    double a, b, c, d, e, f, g, h, i;
    double j, k, l;
    double beta, gamma, t;
    //TODO make these references
    const Vector3D& orig = ray.getOrigin();
    const Vector3D& dir = ray.getDirection();

    a = v1->location[0]-v2->location[0]; //x1 -x2
    b = v1->location[1]-v2->location[1]; //y1 -y2
    c = v1->location[2]-v2->location[2]; //z1 -z2
    d = v1->location[0]-v3->location[0]; //x1 -x3
    e = v1->location[1]-v3->location[1]; //y1 -y3
    f = v1->location[2]-v3->location[2]; //z1 -z3
    g = -1.0* dir[0];
    h = -1.0* dir[1];
    i = -1.0* dir[2];
    j = v1->location[0]- orig[0];
    k = v1->location[1]- orig[1];
    l = v1->location[2]- orig[2];

    double M = a*(e*i-h*f)+b*(g*f-d*i)+c*(d*h-e*g);

    t = (f*(a*k-j*b)+e*(j*c-a*l)+d*(b*l-k*c))/M; //plane intersection
    if(t<tmin | t > tmax)
    {
        return false;
    }
    gamma = (i*(a*k-j*b)+h*(j*c-a*l)+g*(b*l-k*c))/M; //test v1 v2 edge
    if(gamma < 0 | gamma > 1)
    {
        return false;
    }
    beta = (j*(e*i-h*f)+k*(g*f-d*i)+l*(d*h-e*g))/M; //test v1 v3 edge
    if(beta < 0 | beta > (1-gamma))
    {
        return false;
    }
    else
    {
        ir.tVal = t;
        ir.shape = this;
        ir.ray = ip.getRay();
        ir.hitPoint = ir.ray.follow(t);       
        if (v1->hasNormal&&v2->hasNormal&&v3->hasNormal) {
            //interpolate the normal
            //gamma == 1 means v3
            //beta == 1 means v2
            //both == 0 means v1
            ir.normal = 
                    (gamma*v3->normal 
                    + beta*v2->normal 
                    + (1-gamma-beta)*v1->normal);
            ir.normal.normalize();
            
        }
        else {
            Vector3D normal;
            Vector3D E1 = v2->location - v1->location;
            Vector3D E2 = v3->location - v2->location;
            normal = E1.cross(E2);
            normal.normalize(); 
            ir.normal = normal;
        }

        if(v1->hasTexture && v2->hasTexture && v3->hasTexture){
            //interpolate texture coords
            ir.textureCoords =
                    (gamma*v3->textureMap
                    + beta*v2->textureMap
                    + (1-gamma-beta)*v1->textureMap);
        }
        else{
            ir.textureCoords = Vector3D(0,0,0);
        }
        
        return true;
    }
}

void Triangle::buildBoundingBox(){
    double xmin = min(v1->location[0], min(v2->location[0], v3->location[0]));
    double xmax = max(v1->location[0], max(v2->location[0], v3->location[0]));
    double ymin = min(v1->location[1], min(v2->location[1], v3->location[1]));
    double ymax = max(v1->location[1], max(v2->location[1], v3->location[1]));
    double zmin = min(v1->location[2], min(v2->location[2], v3->location[2]));
    double zmax = max(v1->location[2], max(v2->location[2], v3->location[2]));
    boundingBox = BoundingBox(Vector3D(xmin, ymin, zmin), Vector3D(xmax, ymax, zmax));
    boundingBox.pad(FLT_EPSILON);
}
